//==========================================================================
// Copyright (c) 2000-2009,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <linknode.h>
#include <elapi.h>
#include <pseudo.h>
#include <aura.h>

struct NamingObject : public DLinkNode
{
    PInterface  m_pObject;
    wchar_t     m_wszName[1];

    static NamingObject *Find(const wchar_t *);
};

static DLinkNode s_namingObjectList;
static Aura_Handle s_namingObjectListLock;

NamingObject *NamingObject::Find(const wchar_t *pwszName)
{
    DLinkNode *pNode;

    pNode = s_namingObjectList.Next();
    while (pNode != &s_namingObjectList) {
        if (0 == wcscmp(pwszName, ((NamingObject *)pNode)->m_wszName)) {
            return (NamingObject *)pNode;
        }
        pNode = pNode->Next();
    }
    return NULL;
}

struct ObjectWaiter : public DLinkNode
{
    wchar_t     *m_pwszName;
    Aura_Handle m_hEvent;
};

static DLinkNode s_objectWaiterList;
static Aura_Handle s_objectWaiterListLock;

ECode InitRunningObjectTable()
{
    ECode ec;

    ec = AuraCriticalSection_Create(&s_namingObjectListLock);
    if (FAILED(ec)) return ec;

    ec = AuraCriticalSection_Create(&s_objectWaiterListLock);
    if (FAILED(ec)) return ec;

    s_namingObjectList.Initialize();
    s_objectWaiterList.Initialize();
    return NOERROR;
}

void DestroyRunningObjectTable()
{
    if (NULL != s_namingObjectListLock) {
        AuraCriticalSection_Destroy(s_namingObjectListLock);
    }
    if (NULL != s_objectWaiterListLock) {
        AuraCriticalSection_Destroy(s_objectWaiterListLock);
    }

    // TODO: deal with ROT and object waiter list.
}

ELAPI CPseudoKernel::RegisterRunningObject(
    WString name,
    PInterface pObject)
{
    NamingObject *pNamingObject;
    ObjectWaiter *pObjectWaiter;

    AuraCriticalSection_Enter(s_namingObjectListLock);
    pNamingObject = NamingObject::Find(name);
    if (NULL != pNamingObject) {
        AuraCriticalSection_Leave(s_namingObjectListLock);
        return E_ALREADY_EXIST;
    }

    pNamingObject = (NamingObject *) \
        malloc(sizeof(NamingObject) + sizeof(wchar_t) * wcslen(name));
    if (!pNamingObject) {
        AuraCriticalSection_Leave(s_namingObjectListLock);
        return E_OUT_OF_MEMORY;
    }

    wcscpy(pNamingObject->m_wszName, name);
    pNamingObject->m_pObject = pObject;
    pObject->AddRef();

    s_namingObjectList.InsertLast((DLinkNode *)pNamingObject);
    AuraCriticalSection_Leave(s_namingObjectListLock);

    AuraCriticalSection_Enter(s_objectWaiterListLock);
    ForEachDLinkNode(ObjectWaiter *, pObjectWaiter, &s_objectWaiterList) {
        if (0 == wcscmp(name, pObjectWaiter->m_pwszName)) {
            AuraEvent_Signal(pObjectWaiter->m_hEvent, true);
        }
    }
    AuraCriticalSection_Leave(s_objectWaiterListLock);

    return NOERROR;
}

ELAPI CPseudoKernel::FindRunningObject(
            WString name,
            PInterface *ppObject)
{
    NamingObject *pNamingObject;

    AuraCriticalSection_Enter(s_namingObjectListLock);
    pNamingObject = NamingObject::Find(name);
    if (NULL == pNamingObject) {
        AuraCriticalSection_Leave(s_namingObjectListLock);
        return E_DOES_NOT_EXIST;
    }

    *ppObject = pNamingObject->m_pObject;
    (*ppObject)->AddRef();
    AuraCriticalSection_Leave(s_namingObjectListLock);

    return NOERROR;
}

ELAPI CPseudoKernel::WaitForRunningObject(
            WString name, Int32 timeout, WaitResult *pResult)
{
    ECode ec;
    ObjectWaiter waiter;

    AuraCriticalSection_Enter(s_objectWaiterListLock);
    AuraCriticalSection_Enter(s_namingObjectListLock);

    if (NULL != NamingObject::Find(name)) {
        AuraCriticalSection_Leave(s_namingObjectListLock);
        AuraCriticalSection_Leave(s_objectWaiterListLock);
        *pResult = WaitResult_OK;
        return NOERROR;
    }
    AuraCriticalSection_Leave(s_namingObjectListLock);

    ec = AuraEvent_Create(false, 0, &waiter.m_hEvent);
    if (FAILED(ec)) {
        AuraCriticalSection_Leave(s_objectWaiterListLock);
        return ec;
    }
    waiter.m_pwszName = (wchar_t*)_alloca((name.GetLength()+1)*sizeof(wchar_t));
    wcscpy(waiter.m_pwszName, name);

    s_objectWaiterList.InsertLast(&waiter);
    AuraCriticalSection_Leave(s_objectWaiterListLock);

    Aura_EventState state;
    AuraEvent_Wait(
            waiter.m_hEvent, timeout, (Aura_WaitResult *)pResult, &state);

    AuraCriticalSection_Enter(s_objectWaiterListLock);
    waiter.Detach();
    AuraCriticalSection_Leave(s_objectWaiterListLock);
    AuraEvent_Destroy(waiter.m_hEvent);

    return NOERROR;
}

ELAPI CPseudoKernel::UnregisterRunningObject(
            WString name)
{
    NamingObject *pNamingObject;

    AuraCriticalSection_Enter(s_namingObjectListLock);
    pNamingObject = NamingObject::Find(name);
    if (NULL == pNamingObject) {
        AuraCriticalSection_Leave(s_namingObjectListLock);
        return E_DOES_NOT_EXIST;
    }
    pNamingObject->Detach();
    AuraCriticalSection_Leave(s_namingObjectListLock);

    pNamingObject->m_pObject->Release();
    free(pNamingObject);

    return NOERROR;
}
